/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ytmc.uploader;

import com.google.gdata.client.GoogleService.CaptchaRequiredException;
import com.google.gdata.client.youtube.YouTubeService;
import com.google.gdata.util.AuthenticationException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.FileConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import ytmc.setup.*;
import ytmc.FileSize;
import ytmc.Model;
import ytmc.TransferSpeed;

/**
 *
 * @author K
 */
public class UploaderModel extends AbstractTableModel implements Model
{

    private String[] columnNames = { "Nazwa pliku", "Tytuł", "Kategoria", "Tagi", "Opis", "Status", "Rozmiar", "Postęp", "Prędkość", "Szacowane"};
    private Class[] columnClasses = { String.class, String.class, String.class, String.class, String.class,
                                      String.class, FileSize.class , String.class , TransferSpeed.class , String.class };

    private YouTubeService youTubeService;
    private Authentication auth;
    private final String developersKey = "AI39si5vkI-xc6wsH6vV5ZuY3ez0OGfQ5C0UOEnwCKnQlzPWsVFzVBVS9VPvEKlsNe_JE7JN89u-fZ9_jU1MDztMtSt-DcOHEA";

    private ArrayList<UploadTask> tasks;

    UploaderModel()
    {
        this.youTubeService = new YouTubeService("YouTube Management Center", developersKey);
        this.auth = new Authentication(youTubeService);
        this.tasks = new ArrayList<UploadTask>();
        loadTasks();
    }

    public void addTask(UploadTask task)
    {
        tasks.add(task);
        int rowNumber = tasks.size() - 1;
        fireTableRowsInserted(rowNumber, rowNumber);
        saveTask(task);
    }

    public void addTasks(Collection<UploadTask> uploadTasks)
    {
        int rowStart = tasks.size();
        tasks.addAll(uploadTasks);
        fireTableRowsInserted(rowStart, tasks.size());
        saveTasks();
    }

    public void removeTask(int index)
    {
        if (index >= 0 && index < tasks.size())
        {
            tasks.remove(index);
            fireTableRowsDeleted(index, index);
            saveTasks();
        }
    }

    public void startTask(int index) throws CaptchaRequiredException, AuthenticationException
    {
        if (index >= 0 && index < tasks.size())
        {
            auth.Authenticate(new Credentials());

            final UploadTask task = tasks.get(index);
            UploadTaskRunner runner = new UploadTaskRunner(task, youTubeService);
            runner.addPropertyChangeListener(new PropertyChangeListener()
            {
                @Override
                public void propertyChange(PropertyChangeEvent evt)
                {
                    if (evt.getPropertyName().equals("task"))
                    {
                        fireTableRowsUpdatedOnDispatcher(task);
                    }
                    else if (evt.getPropertyName().equals("finished"))
                    {
                        saveTask(task);
                    }
                }
            });

            runner.uploadAsync();
        }
    }

    public void submitCaptcha(String captchaToken, String captchaAnswer) throws CaptchaRequiredException, AuthenticationException
    {
        auth.Authenticate(new Credentials(), captchaToken, captchaAnswer);
    }

    private void fireTableRowsUpdatedOnDispatcher(final UploadTask task)
    {
        try
        {
            SwingUtilities.invokeAndWait(new Runnable()
            {
                @Override
                public void run()
                {
                    int index = tasks.indexOf(task);
                    fireTableRowsUpdated(index, index);
                }
            });
        }
        catch (InterruptedException ex)
        {
            Logger.getLogger(UploaderModel.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (InvocationTargetException ex)
        {
            Logger.getLogger(UploaderModel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void saveTask(UploadTask task)
    {
       XMLConfiguration config = Config.load();

        try
        {
            int i = tasks.indexOf(task);

            UploadTaskStatus status = task.getStatus();
            if (status != UploadTaskStatus.FINISHED)
            {
                status = UploadTaskStatus.NOT_STARTED;
            }

            config.setProperty("upload.tasks.task{" + i + "}.file", task.getFilePath());
            config.setProperty("upload.tasks.task{" + i + "}.title", task.getTitle());
            config.setProperty("upload.tasks.task{" + i + "}.category", task.getCategory().getYouTubeName());
            config.setProperty("upload.tasks.task{" + i + "}.tags", task.getTags());
            config.setProperty("upload.tasks.task{" + i + "}.description", task.getDescription());
            config.setProperty("upload.tasks.task{" + i + "}.status", status);
            config.setProperty("upload.tasks.task{" + i + "}.size", task.getSize().getSize());
            config.setProperty("upload.tasks.task{" + i + "}.progress", task.getProgress());

            config.save();
        }
        catch (ConfigurationException ex)
        {
            Logger.getLogger(UploaderModel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void saveTasks()
    {
        XMLConfiguration config = Config.load();

        try
        {
            config.clearTree("upload.tasks");

            int i = 0;
            for (UploadTask task : tasks)
            {
                UploadTaskStatus status = task.getStatus();
                if (status != UploadTaskStatus.FINISHED)
                {
                    status = UploadTaskStatus.NOT_STARTED;
                }

                config.setProperty("upload.tasks.task{" + i + "}.file", task.getFilePath());
                config.setProperty("upload.tasks.task{" + i + "}.title", task.getTitle());
                config.setProperty("upload.tasks.task{" + i + "}.category", task.getCategory().getYouTubeName());
                config.setProperty("upload.tasks.task{" + i + "}.tags", task.getTags());
                config.setProperty("upload.tasks.task{" + i + "}.description", task.getDescription());
                config.setProperty("upload.tasks.task{" + i + "}.status", status);
                config.setProperty("upload.tasks.task{" + i + "}.size", task.getSize().getSize());
                config.setProperty("upload.tasks.task{" + i + "}.progress", task.getProgress());

                i++;
            }

            config.save();
        }
        catch (ConfigurationException ex)
        {
            Logger.getLogger(UploaderModel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void loadTasks()
    {
        tasks.clear();
        boolean isTaskRemoved = false;
        FileConfiguration config = Config.load();

            List savedTasks = config.getList("upload.tasks.task.file");
            if (savedTasks != null)
            {
                for (int i = 0; i < savedTasks.size(); i++)
                {
                    String path = config.getString("upload.tasks.task{" + i + "}.file");
                    String title = config.getString("upload.tasks.task{" + i + "}.title");
                    VideoCategory category = VideoCategory.fromString(config.getString("upload.tasks.task{" + i + "}.category"));
                    String tags = config.getString("upload.tasks.task{" + i + "}.tags");
                    String description = config.getString("upload.tasks.task{" + i + "}.description");

                    UploadTask task;
                    try
                    {
                        task = new UploadTask(path, title, category, tags, description);
                        task.setStatus(UploadTaskStatus.valueOf(config.getString("upload.tasks.task{" + i + "}.status")));
                        task.setProgress(config.getInt("upload.tasks.task{" + i + "}.progress"));
                        tasks.add(task);
                    }
                    catch (IOException ex)
                    {
                        isTaskRemoved = true;
                        Logger.getLogger(UploaderModel.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }

                if(isTaskRemoved)
                {
                    saveTasks();
                }
            }
    }

    @Override
    public Class<?> getColumnClass(int i)
    {
        return columnClasses[i];
    }

    @Override
    public String getColumnName(int i)
    {
        return columnNames[i];
    }

    @Override
    public int getRowCount()
    {
        return tasks.size();
    }

    @Override
    public int getColumnCount()
    {
        return columnNames.length;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex)
    {
        if (rowIndex < 0 || rowIndex >= getRowCount())
        {
            return null;
        }

        switch(columnIndex)
        {
            case 0:
                return (new File(tasks.get(rowIndex).getFilePath())).getName();
            case 1:
                return tasks.get(rowIndex).getTitle();
            case 2:
                return tasks.get(rowIndex).getCategory().getFriendlyName();
            case 3:
                return tasks.get(rowIndex).getTags();
            case 4:
                return tasks.get(rowIndex).getDescription();
            case 5:
                return tasks.get(rowIndex).getStatus().getFriendlyName();
            case 6:
                return tasks.get(rowIndex).getSize();
            case 7:
                return String.format("%d %%", tasks.get(rowIndex).getProgress());
            case 8:
                return tasks.get(rowIndex).getUploadSpeed();
            case 9:
                return tasks.get(rowIndex).getRemainingTime().toString();
            default:
                return null;
        }
    }
}
